streaming code used to hang when it was asked to "exit" - fixed now#635
streaming code used to hang when it was asked to "exit" - fixed now#635
Conversation
speech/grpc/transcribe_streaming.py
Outdated
|
|
||
| audio_stream.stop_stream() | ||
| audio_stream.close() | ||
|
|
There was a problem hiding this comment.
Was this move necessary? I'd prefer to have the function that opened the stream be responsible for closing it.
There was a problem hiding this comment.
I agree with you. But, the thread hangs and doesn't come out of the while loop. So, I had to move here and close when I get out.
There was a problem hiding this comment.
Ah - this is because buff.put(None) is never executed if the above loop is exited because stoprequest.is_set() is True. (this means the blocking buff.get() in _audio_data_generator blocks forever, since it's never given the None to exit from its loop).
If you change the above loop to:
try:
...
except IOError:
pass
finally:
buff.put(None)then you should be able to put this back.
There was a problem hiding this comment.
No, this happens because the stream is stopped and the thread hangs when this thread is still in the loop.
| audio_stream.stop_stream() | ||
| audio_stream.close() | ||
| fill_buffer_thread.join() | ||
| audio_interface.terminate() |
There was a problem hiding this comment.
Any particular reason to remove this line? It seems like good hygiene..
There was a problem hiding this comment.
That might have been a mistake. I will restore it.
speech/grpc/transcribe_streaming.py
Outdated
| """ | ||
| while True: | ||
| # Use a blocking get() to ensure there's at least one chunk of data | ||
| while not stoprequest.isSet(): |
There was a problem hiding this comment.
prefer .is_set, for python style conventions. Ditto below.
speech/grpc/transcribe_streaming.py
Outdated
|
|
||
|
|
||
| def _audio_data_generator(buff): | ||
| def _audio_data_generator(buff, stoprequest): |
There was a problem hiding this comment.
_audio_data_generator shouldn't need this. Once _fill_buffer gets the signal, it'll add None to the buffer, which will cause this generator to exit.
There was a problem hiding this comment.
But, fill_buffer doesn't add the None unless it receives IOError which it doesn't. Ok let me make one change to _fill_buffer.
There was a problem hiding this comment.
Actually to think of it making both threads stop from the same signal is better.
There was a problem hiding this comment.
Can you explain your reasoning?
There was a problem hiding this comment.
The reasoning: Both reader and writer threads consider the stopsignal. If reader looks at the indirect signal from the writer, we don't know if it needed to stop because there is really a stop signal or an error in the writer's output.
There was a problem hiding this comment.
Hm... sure, okay, I can see that. I think there's a case for just letting _fill_buffer propagate the way it is, but I won't block on it.
There was a problem hiding this comment.
I actually had thought about both cases, but can now also see the case for other option. And now as I am thinking more I think the other option makes more sense :) Let me go back to it.
|
Yes I was able to reproduce this. It did hang on my mac. |
speech/grpc/transcribe_streaming.py
Outdated
| while True: | ||
| # Use a blocking get() to ensure there's at least one chunk of data | ||
| while not stoprequest.is_set(): | ||
| # Use a blocking get() to ensure there's at least one chunk of data |
There was a problem hiding this comment.
Please remove trailing whitespace
speech/grpc/transcribe_streaming.py
Outdated
|
|
||
|
|
||
| def _audio_data_generator(buff): | ||
| def _audio_data_generator(buff, stoprequest): |
There was a problem hiding this comment.
Hm... sure, okay, I can see that. I think there's a case for just letting _fill_buffer propagate the way it is, but I won't block on it.
speech/grpc/transcribe_streaming.py
Outdated
|
|
||
| audio_stream.stop_stream() | ||
| audio_stream.close() | ||
|
|
There was a problem hiding this comment.
Ah - this is because buff.put(None) is never executed if the above loop is exited because stoprequest.is_set() is True. (this means the blocking buff.get() in _audio_data_generator blocks forever, since it's never given the None to exit from its loop).
If you change the above loop to:
try:
...
except IOError:
pass
finally:
buff.put(None)then you should be able to put this back.
speech/grpc/transcribe_streaming.py
Outdated
| """ | ||
| while True: | ||
| # Use a blocking get() to ensure there's at least one chunk of data | ||
| # Use a blocking get() to ensure there's at least one chunk of data |
|
|
||
| yield _audio_data_generator(buff) | ||
|
|
||
| audio_stream.stop_stream() |
There was a problem hiding this comment.
Any particular reason not to stop the stream before closing? While I'm not certain it's necessary, all the examples stop the stream before closing it.
There was a problem hiding this comment.
Once the stream is stopped, one may not call write or read. And thread _fill_buffer gets stuck.
| except IOError: | ||
| # This happens when the stream is closed. Signal that we're done. | ||
| buff.put(None) | ||
| pass |
There was a problem hiding this comment.
Without this, you'll have a race condition. ie if _audio_data_generator's buff.get() is waiting for data in the buffer when stoprequest is triggered, it could potentially wait forever and never yield a value.
jerjou
left a comment
There was a problem hiding this comment.
Nice - thanks for catching that race condition 😁
Looks good - just some polishing of the comments left.
| # This happens when the stream is closed. Signal that we're done. | ||
| pass | ||
| finally: | ||
| buff.put(None) |
There was a problem hiding this comment.
A comment here describing what putting None means would be helpful.
speech/grpc/transcribe_streaming.py
Outdated
| # First, a thread that collects audio data as it comes in | ||
| with record_audio(RATE, CHUNK) as buffered_audio_data: | ||
|
|
||
| # stop request |
There was a problem hiding this comment.
Can you make this comment describe what stoprequest is for? Simply repeating the name of the variable isn't helpful.
speech/grpc/transcribe_streaming.py
Outdated
| @@ -208,7 +212,11 @@ def main(): | |||
| make_channel('speech.googleapis.com', 443)) as service: | |||
| # For streaming audio from the microphone, there are three threads. | |||
| # First, a thread that collects audio data as it comes in | |||
There was a problem hiding this comment.
This comment should be right above the with block, since that's what it's describing.
speech/grpc/transcribe_streaming.py
Outdated
| except queue.Empty: | ||
| break | ||
|
|
||
| # If the data contains None then set stop = True. |
There was a problem hiding this comment.
Comments should describe the meaning and purpose of the code, not just narrate what the code is doing. Maybe something like:
# If `_fill_buffer` adds `None` to the buffer, the audio stream is closed.
# Yield the final bit of the buffer and exit the loop.
| # If the data contains None then set stop = True. | ||
| if None in data: | ||
| stop = True | ||
| data.remove(None) |
speech/grpc/transcribe_streaming.py
Outdated
|
|
||
| def main(): | ||
| # For streaming audio from the microphone, there are three threads. | ||
| # First, a thread that collects audio data as it comes in |
There was a problem hiding this comment.
Sorry - I meant that this comment should be on the below with statement, where it was before. ie since the comment is describing the thread that collects audio data, it should be just before the record_audio call.
speech/grpc/transcribe_streaming.py
Outdated
| with record_audio(RATE, CHUNK) as buffered_audio_data: | ||
|
|
||
| # stoprequest is event object which is set in `listen_print_loop`. | ||
| # To indicate that the trancsription should be stopped. |
There was a problem hiding this comment.
trancsription -> transcription
speech/grpc/transcribe_streaming.py
Outdated
| with record_audio(RATE, CHUNK) as buffered_audio_data: | ||
|
|
||
| # stoprequest is event object which is set in `listen_print_loop`. | ||
| # To indicate that the trancsription should be stopped. |
There was a problem hiding this comment.
Line 219 and 220 are the same sentence (otherwise, line 220 is a sentence fragment). Please change to:
# stoprequest is an event object which is set in `listen_print_loop` to indicate that the transcription should be stopped.
speech/grpc/transcribe_streaming.py
Outdated
|
|
||
| # stoprequest is event object which is set in `listen_print_loop`. | ||
| # To indicate that the trancsription should be stopped. | ||
| # `_fill_buffer` checks and stops collecting data from audio_stream. |
There was a problem hiding this comment.
I had a bunch of trouble parsing this sentence. Perhaps:
# The `_fill_buffer` thread checks this object, and closes the `audio_stream` once it's set.
|
@dpebot merge when green. |
|
Okay! I'll merge when all statuses are green. |
|
Does dpebot know he can also merge when someone approves the PR + green status? |
|
I'm not sure what you're asking. Are you suggesting that we should merge PRs before the automated tests pass? |
|
I am asking can dpebot take over when a) someone has approved the PR b) green status. Both a and b are true. |
|
I don't understand what you're saying. Are you stating that - currently - someone has approved this PR AND the PR's status is green? Or are you asking whether dpebot can submit the PR if a OR b are true? Or are you asking whether dpebot can submit the PR when a AND b are true? |
|
If it helps - IIUC, dpebot just waits for travis to turn green before submitting. Currently travis is still waiting for test results, so dpebot is not merging the PR yet. |
|
I thought my question was simple :) In other words I am asking. Do we have to ask dpebot explicitly "merge when everything is green"? Can dpebot not figure that out automatically and do the needful? |
|
I think your question is simple, but you're not expressing the full question, or you're not giving enough context for the question, so it's hard to figure out what you're asking. Perhaps it'd be helpful to structure your questions into three steps:
For example:
It was ambiguous in your original question what you thought the current situation was, and what you expected the situation to be. For instance - what did you mean by 'also'? That implies that there's another situation wherein dpebot will merge, besides when it's approved + green; but you don't describe this other situation you allude to, so there's no way for me to know what you're contrasting. Does that make sense? To answer your question - dpebot listens for travis to complete, and only merges if it has |
|
Wow! Travis is sloooow! |
|
Yeah - it's usually one slow-running project in our org that backs everything else up :-/ We might consider changing to circleci, like some of the other teams are doing.. |
@jerjou @jonparrott PTAL